package fuzion24.device.vulnerability.test.ui;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;

import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.evernote.android.job.JobManager;
import com.evernote.android.job.JobRequest;
import com.nowsecure.android.vts.R;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import fuzion24.device.vulnerability.test.ResultsCallback;
import fuzion24.device.vulnerability.test.ShareViaGoogleForm;
import fuzion24.device.vulnerability.test.VulnerabilityTestResult;
import fuzion24.device.vulnerability.test.VulnerabilityTestRunner;
import fuzion24.device.vulnerability.test.adapter.RecyclerAdapter;
import fuzion24.device.vulnerability.test.job.AndroidVTSJobCreator;
import fuzion24.device.vulnerability.test.job.CheckForUpdatesJob;
import fuzion24.device.vulnerability.util.DeviceInfo;
import fuzion24.device.vulnerability.util.SharedPreferencesUtils;
import fuzion24.device.vulnerability.vulnerabilities.VulnerabilityResultSerialzier;

public class MainActivity extends AppCompatActivity {

    private static final String SERIALIZABLE_RESULTS = "SERIALIZABLE_RESULTS";

    private static final String TAG = "VULN_TEST";

    private static final int CREATE_CHECK_FOR_UPDATES_JOB = 1;
    private static final int CHECK_FOR_UPDATES = 2;

    private DeviceInfo devInfo;
    private ArrayList<VulnerabilityTestResult> testResults;
    private TextView emptyView;
    private CoordinatorLayout coordinatorLayout;
    private RecyclerAdapter recyclerAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        toolbar.setTitleTextColor(Color.WHITE);
        setSupportActionBar(toolbar);
        getSupportActionBar().setTitle(R.string.app_name);

        if (savedInstanceState != null && savedInstanceState.containsKey(SERIALIZABLE_RESULTS)) {
            testResults = (ArrayList<VulnerabilityTestResult>) savedInstanceState.getSerializable(SERIALIZABLE_RESULTS);
        } else {
            testResults = new ArrayList<>();
        }

        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        coordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinatorLayout);
        emptyView = (TextView) findViewById(R.id.emptyView);
        recyclerAdapter = new RecyclerAdapter(MainActivity.this, testResults);

        recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
        recyclerView.setAdapter(recyclerAdapter);

        final TextView tvKernelVersion = (TextView) findViewById(R.id.kernelVersion);
        final TextView tvBuildFingerPrint = (TextView) findViewById(R.id.buildFingerPrint);
        final TextView tvBuildID = (TextView) findViewById(R.id.buildID);
        final TextView tvBuildBrand = (TextView) findViewById(R.id.buildBrand);
        final TextView tvBuildManuf = (TextView) findViewById(R.id.buildManufacturer);
        final TextView tvBuildModel = (TextView) findViewById(R.id.buildModel);
        final TextView tvBuildRelease = (TextView) findViewById(R.id.buildRelease);
        final TextView tvBuildSDK = (TextView) findViewById(R.id.buildSDK);
        final TextView tvBuildABIList = (TextView) findViewById(R.id.buildABIList);

        devInfo = DeviceInfo.getDeviceInfo();

        tvBuildFingerPrint.setText(devInfo.getBuildFingerPrint());
        tvBuildID.setText(devInfo.getBuildID());
        tvKernelVersion.setText(devInfo.getKernelVersion());
        tvBuildBrand.setText(devInfo.getBuildBrand());
        tvBuildManuf.setText(devInfo.getBuildManufacturer());
        tvBuildModel.setText(devInfo.getBuildModel());
        tvBuildRelease.setText(devInfo.getBuildRelease());
        tvBuildSDK.setText(devInfo.getBuildSDK());

        StringBuilder sb = new StringBuilder();

        for (String s : devInfo.getSupportedABIS()) {
            sb.append(s);
            sb.append(" ");
        }

        tvBuildABIList.setText(sb.toString());

        FloatingActionButton fabStart = (FloatingActionButton) findViewById(R.id.fabStart);

        fabStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runTestsSuit();
            }
        });

        // Creates the jobs to check for updates.
        JobManager.instance().cancelAllForTag(CheckForUpdatesJob.TAG);

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                == PackageManager.PERMISSION_GRANTED) {
            createCheckForUpdatesJob();
        } else {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                CREATE_CHECK_FOR_UPDATES_JOB);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_menu, menu);

        menu.getItem(2).setChecked(SharedPreferencesUtils.isAutomaticSharingEnable(MainActivity.this));

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int itemId = item.getItemId();

        switch (itemId) {
            case R.id.menu_export_results:
            case R.id.menu_share_results:
                if (testResults == null || testResults.isEmpty()) {
                    Snackbar.make(coordinatorLayout, R.string.run_tests, Snackbar.LENGTH_LONG).setAction(R.string.start, new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            runTestsSuit();
                        }

                    }).show();

                    return true;
                }

                Intent intent = null;
                try {
                    JSONObject json = VulnerabilityResultSerialzier.serializeResultsToJson(testResults, devInfo);
                    if (itemId == R.id.menu_export_results) {
                        intent = new Intent(Intent.ACTION_SEND);
                        intent.setType("text/plain");
                        intent.putExtra(Intent.EXTRA_SUBJECT, "Android VTS Results");
                        intent.putExtra(Intent.EXTRA_TEXT, json.toString(4));
                    } else if (itemId == R.id.menu_share_results) {
                        Uri formUri = new ShareViaGoogleForm(json).buildUri();
                        intent = new Intent(Intent.ACTION_VIEW, formUri);
                    }
                } catch (JSONException e) {
                    Log.d(TAG, "Json exception: " + e.getMessage());
                    Snackbar.make(coordinatorLayout, R.string.error_exporting_results, Snackbar.LENGTH_LONG).show();
                }

                if (intent != null) {
                    startActivity(Intent.createChooser(intent, getString(R.string.share_results_via)));
                }

                return true;

            case R.id.menu_automatic_share_results:
                new MaterialDialog.Builder(this)
                    .title(R.string.dialog_automatic_sharing_title)
                    .content(R.string.dialog_automatic_sharing_description)
                    .positiveText(R.string.global_sharing)
                    .negativeText(R.string.global_notsharing)
                    .onPositive(new MaterialDialog.SingleButtonCallback() {
                        @Override
                        public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
                            SharedPreferencesUtils.setIsAutomaticSharingEnable(MainActivity.this, true);
                        }
                    })
                    .onNegative(new MaterialDialog.SingleButtonCallback() {
                        @Override
                        public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
                            SharedPreferencesUtils.setIsAutomaticSharingEnable(MainActivity.this, false);
                        }
                    })
                    .onAny(new MaterialDialog.SingleButtonCallback() {
                        @Override
                        public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
                            invalidateOptionsMenu();
                        }
                    })
                    .show();

                return true;

            case R.id.menu_check_updates:
                if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                        == PackageManager.PERMISSION_GRANTED) {
                    checkForUpdates();
                } else {
                    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                        CHECK_FOR_UPDATES);
                }

                return true;

            case R.id.menu_opensource_licences:
                Intent mainIntent = new Intent(MainActivity.this, OpenSourceLicencesActivity.class);
                startActivity(mainIntent);

                return true;

            default:
                return super.onOptionsItemSelected(item);
        }
    }

    private void runTestsSuit() {
        new VulnerabilityTestRunner(MainActivity.this, true, new ResultsCallback() {
            @Override
            public void finished(final List<VulnerabilityTestResult> results) {
                Log.d(TAG, "Device Vulnerability callback, finished");

                testResults.clear();
                testResults.addAll(results);

                emptyView.setVisibility(View.GONE);
                recyclerAdapter.updateResults(results);

                showScanResume(results);
            }
        }).execute();
    }

    private void showScanResume(final List<VulnerabilityTestResult> results) {
        int numberOfFailed = 0;

        for (VulnerabilityTestResult result : results) {
            if (result.getException() != null) {
                continue;
            }

            if (result.isVulnerable()) {
                numberOfFailed++;
            }
        }

        MaterialDialog.Builder dialogBuilder = new MaterialDialog.Builder(this)
            .title(R.string.scan_details)
            .customView(R.layout.dialog_scan_details_layout, true)
            .positiveText(R.string.dismiss)
            .onAny(new MaterialDialog.SingleButtonCallback() {
                @Override
                public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
                    if (SharedPreferencesUtils.isAutomaticSharingEnable(MainActivity.this)) {
                        try {
                            JSONObject json = VulnerabilityResultSerialzier.serializeResultsToJson(testResults, devInfo);

                            Uri formUri = new ShareViaGoogleForm(json).buildUri();
                            Intent intent = new Intent(Intent.ACTION_VIEW, formUri);

                            startActivity(Intent.createChooser(intent, getString(R.string.share_results_via)));
                        } catch (JSONException exception) {
                            exception.printStackTrace();
                        }
                    }
                }
            });

        if (numberOfFailed > 0) {
            dialogBuilder.negativeText(R.string.my_device_is_vulnerable_info)
                .onNegative(new MaterialDialog.SingleButtonCallback() {
                    @Override
                    public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
                        Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.nowsecure.com/blog/2015/11/18/my-device-is-vulnerable-now-what/"));
                        startActivity(browserIntent);
                    }
                });
        }

        View view = dialogBuilder.show().getCustomView();

        TextView state = (TextView) view.findViewById(R.id.scan_details_device_state);
        if (numberOfFailed > 0) {
            state.setText(R.string.scan_details_vulnerable_message);
            state.setTextColor(getResources().getColor(R.color.red));
        } else {
            state.setText(R.string.scan_details_secure_message);
            state.setTextColor(getResources().getColor(R.color.green));
        }

        TextView numberOfTests = (TextView) view.findViewById(R.id.scan_details_number_tests);
        numberOfTests.setText(String.valueOf(results.size()));

        TextView numberOfFailedTests = (TextView) view.findViewById(R.id.scan_details_failed_tests);
        numberOfFailedTests.setText(String.valueOf(numberOfFailed));
    }

    private void createCheckForUpdatesJob() {
        new JobRequest.Builder(CheckForUpdatesJob.TAG)
            .setPeriodic(TimeUnit.DAYS.toMillis(7))
            .setRequirementsEnforced(true)
            .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
            .setPersisted(true)
            .build()
            .schedule();
    }

    private void checkForUpdates() {
        final MaterialDialog progressDialog = new MaterialDialog.Builder(this)
            .title(R.string.loading)
            .content(R.string.checking_updates)
            .cancelable(false)
            .progress(true, 0)
            .show();

        CheckForUpdatesJob.checkForUpdates(this, progressDialog);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case CREATE_CHECK_FOR_UPDATES_JOB:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    createCheckForUpdatesJob();
                }
                break;

            case CHECK_FOR_UPDATES:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    checkForUpdates();
                }
                break;
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
    }

    @Override
    protected void onSaveInstanceState(final Bundle outState) {
        outState.putSerializable(SERIALIZABLE_RESULTS, testResults);
    }

}
